package paginator

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/spf13/cast"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"math"
	"strings"
	"wskyygyso/api/pkg/config"
	"wskyygyso/api/pkg/logger"
)

type Paging struct {
	CurrentPage int    //当前页
	PerPage     int    //每页条数
	TotalPage   int    //总页数
	TotalCount  int64  //总数据数
	NextPageUrl string //下一页链接
	PrevPageUrl string //上一页链接
}

type Paginator struct {
	BaseUrl    string       //base Url
	PerPage    int          //每页条数
	Page       int          //当前页
	Offset     int          //数据库读取数据时 limit 数
	TotalCount int64        //数据总数
	TotalPage  int          //总页数
	Sort       string       //排序规则
	Order      string       //排序字段
	query      *gorm.DB     //db query 句柄
	ctx        *gin.Context //gin context
}

func (p Paginator) getPerPage(perPage int) int {
	queryPerPage := p.ctx.Query(config.Get("paging.url_query_per_page"))
	if len(queryPerPage) > 0 {
		perPage = cast.ToInt(queryPerPage)
	}
	if perPage <= 0 {
		perPage = config.GetInt("paging.perpage")
	}
	return perPage
}

func (p Paginator) getCurrentPage() int {
	page := cast.ToInt(p.ctx.Query(config.Get("paging.url_query_page")))
	if page <= 0 {
		page = 1
	}
	if p.TotalPage == 0 {
		return 0
	}
	if page > p.TotalPage {
		return p.TotalPage
	}
	return page
}

func (p Paginator) getTotalCount() int64 {
	//查询句柄 获取总数据数
	var count int64
	if err := p.query.Count(&count).Error; err != nil {
		return 0
	}
	return count
}

func (p Paginator) getTotalPage() int {
	if p.TotalCount == 0 {
		return 0
	}
	nums := int64(math.Ceil(float64(p.TotalCount) / float64(p.PerPage)))
	if nums == 0 {
		nums = 1
	}
	return int(nums)
}

func (p Paginator) getFormatBaseUrl(baseURL string) string {
	if strings.Contains(baseURL, "?") {
		baseURL = baseURL + "&" + config.Get("paging.url_query_page") + "="
	} else {
		baseURL = baseURL + "?" + config.Get("paging.url_query_page") + "="
	}
	return baseURL
}

func (p Paginator) getPageLink(page int) string {
	return fmt.Sprintf("%v%v&%s=%s&%s=%s&%s=%v",
		p.BaseUrl,
		page,
		config.Get("paging.url_query_sort"),
		p.Sort,
		config.Get("paging.url_query_order"),
		p.Order,
		config.Get("paging.url_query_per_page"),
		p.PerPage,
	)
}

func (p Paginator) getNextPageUrl() string {
	if p.TotalPage > p.Page {
		return p.getPageLink(p.Page + 1)
	}
	return ""
}

func (p Paginator) getPrevPageUrl() string {
	if p.Page <= 1 || p.Page >= p.TotalPage {
		return ""
	}
	return p.getPageLink(p.Page - 1)
}

func (p *Paginator) initProperties(perPage int, baseUrl string) {
	p.BaseUrl = p.getFormatBaseUrl(baseUrl)
	p.PerPage = p.getPerPage(perPage)
	p.Order = p.ctx.DefaultQuery(config.Get("paging.url_query_order"), "id")
	p.Sort = p.ctx.DefaultQuery(config.Get("paging.url_query_sort"), "desc")

	p.TotalCount = p.getTotalCount()
	p.TotalPage = p.getTotalPage()
	p.Page = p.getCurrentPage()
	p.Offset = (p.Page - 1) * p.PerPage
}

func Paginate(c *gin.Context, db *gorm.DB, data interface{}, baseUrl string, perPage int) Paging {
	p := &Paginator{
		ctx:   c,
		query: db,
	}
	p.initProperties(perPage, baseUrl)

	err := p.query.Preload(clause.Associations). // 读取关联
		Order(p.Order + " " + p.Sort). // 排序
		Limit(p.PerPage).
		Offset(p.Offset).
		Find(data).
		Error
	if err != nil {
		logger.LogIf(err)
		return Paging{}
	}
	return Paging{
		CurrentPage: p.Page,
		PerPage:     p.PerPage,
		TotalPage:   p.TotalPage,
		TotalCount:  p.TotalCount,
		NextPageUrl: p.getNextPageUrl(),
		PrevPageUrl: p.getPrevPageUrl(),
	}
}
